<!doctype html><html lang=zh-cn dir=ltr>
<head><meta charset=utf-8>
<meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="目前领域逻辑的组织模式分为三种，“事务脚本”，“领域模型” 以及 “表模块”。
事务脚本类似于面向过程编程，事务脚本有以下优点：
 它是一种大多数软件工程师都能理解的简单过程模型。 它能和一个行数据入口或表数据入口简单的数据源很好的协作。 非常容易设定事务的边界。  一个组数据源操作便是一个独立的事务脚本。 当然事务脚本也存在很大的缺陷，当领域逻辑开始变得复杂时，这些缺点就开始暴露出来。 当几个事务要执行类似的逻辑时，通常几个脚本中会含有某些相同的代码。 通过将这些代码提取出来，来形成公共的子例程，来消除这种情况。 但是，很多时候消除副本会变得棘手，而检测副本则更困难，倒是消除副本后的程序反而比以前还要杂乱无章，难以维护。
复杂的领域逻辑，必然要引入对象，解决前面描述问题的面向对象方法就是领域模型。 一个内容管理系统会有用户，文章等类，进行鉴权，以及写入等逻辑均置于领域模型中。 因此，发布对象调用一次写入方法。 可能还会有其他例程来完成一些读取功能，但它其实都是调用领域模型中已有打方法实现的。
 领域模型的控制不再是由一个过程来控制用户某一个动作的逻辑，而是由每个对象都承担一部分相关逻辑。
 领域模型的开销来自于数据源的复杂度和使用上的复杂性，刚刚接触领域模型的人会需要时间来适应这种思维方式。一旦习惯了，你就会很爽！ 另一方面你需要将数据源映射到领域模型上，数据源越是复杂，领域模型的效果就越是显著。
上为事务脚本
上为领域模型
第三中为领域逻辑的组织模式为表模块，它处于事务脚本和领域模型的一个中间地带。 和领域模型最大的区别就是在表模块中一个表只对应一个实例，而领域模型一行数据便能对应一个实例。
表模块的优点在于可以很容易的和软件架构中已经存在的部分衔接，很多GUI应用都是假定将其与SQL查询结果的记录集结果协同工作的。表模块就工作在记录集之上。你可以很容易的使用。
抉择 别问，问就，直接使用领域模型。
接下来我稍微介绍一下目前各个框架/库在领域逻辑的组织模式上的选择（只列出我用）：
  PHP
 PHP 原生 &amp;lt;事务脚本&amp;gt; ThinkPHP &amp;lt;领域模型&amp;gt; Laravel &amp;lt;领域模型&amp;gt; YII &amp;lt;领域模型&amp;gt;    Java
 java.sql.* &amp;lt;事务脚本&amp;gt; MyBatis &amp;lt;表模块&amp;gt; JPA &amp;lt;领域模型&amp;gt;    Go
 gorm &amp;lt;表模块 | 领域模型&amp;gt; （这个比较神奇）    现在用表模块的人普遍比较多，我曾遇到好几个J2EE工程师都并不喜欢JPA的思维模式。"><title>领域逻辑的组织模式</title><link rel=canonical href=https://sdttttt.github.io/blog/domain-logic-org-mode/>
<link rel=stylesheet href=/scss/style.min.b80bf249ce4a22cf55e8d7340a0b37a2f2c10f54f3a9a49cb94b694a2eb0bbea.css><meta property="og:title" content="领域逻辑的组织模式">
<meta property="og:description" content="目前领域逻辑的组织模式分为三种，“事务脚本”，“领域模型” 以及 “表模块”。
事务脚本类似于面向过程编程，事务脚本有以下优点：
 它是一种大多数软件工程师都能理解的简单过程模型。 它能和一个行数据入口或表数据入口简单的数据源很好的协作。 非常容易设定事务的边界。  一个组数据源操作便是一个独立的事务脚本。 当然事务脚本也存在很大的缺陷，当领域逻辑开始变得复杂时，这些缺点就开始暴露出来。 当几个事务要执行类似的逻辑时，通常几个脚本中会含有某些相同的代码。 通过将这些代码提取出来，来形成公共的子例程，来消除这种情况。 但是，很多时候消除副本会变得棘手，而检测副本则更困难，倒是消除副本后的程序反而比以前还要杂乱无章，难以维护。
复杂的领域逻辑，必然要引入对象，解决前面描述问题的面向对象方法就是领域模型。 一个内容管理系统会有用户，文章等类，进行鉴权，以及写入等逻辑均置于领域模型中。 因此，发布对象调用一次写入方法。 可能还会有其他例程来完成一些读取功能，但它其实都是调用领域模型中已有打方法实现的。
 领域模型的控制不再是由一个过程来控制用户某一个动作的逻辑，而是由每个对象都承担一部分相关逻辑。
 领域模型的开销来自于数据源的复杂度和使用上的复杂性，刚刚接触领域模型的人会需要时间来适应这种思维方式。一旦习惯了，你就会很爽！ 另一方面你需要将数据源映射到领域模型上，数据源越是复杂，领域模型的效果就越是显著。
上为事务脚本
上为领域模型
第三中为领域逻辑的组织模式为表模块，它处于事务脚本和领域模型的一个中间地带。 和领域模型最大的区别就是在表模块中一个表只对应一个实例，而领域模型一行数据便能对应一个实例。
表模块的优点在于可以很容易的和软件架构中已经存在的部分衔接，很多GUI应用都是假定将其与SQL查询结果的记录集结果协同工作的。表模块就工作在记录集之上。你可以很容易的使用。
抉择 别问，问就，直接使用领域模型。
接下来我稍微介绍一下目前各个框架/库在领域逻辑的组织模式上的选择（只列出我用）：
  PHP
 PHP 原生 &amp;lt;事务脚本&amp;gt; ThinkPHP &amp;lt;领域模型&amp;gt; Laravel &amp;lt;领域模型&amp;gt; YII &amp;lt;领域模型&amp;gt;    Java
 java.sql.* &amp;lt;事务脚本&amp;gt; MyBatis &amp;lt;表模块&amp;gt; JPA &amp;lt;领域模型&amp;gt;    Go
 gorm &amp;lt;表模块 | 领域模型&amp;gt; （这个比较神奇）    现在用表模块的人普遍比较多，我曾遇到好几个J2EE工程师都并不喜欢JPA的思维模式。">
<meta property="og:url" content="https://sdttttt.github.io/blog/domain-logic-org-mode/">
<meta property="og:site_name" content="SDTTTTT">
<meta property="og:type" content="article"><meta property="article:section" content="Blog"><meta property="article:tag" content="Design"><meta property="article:published_time" content="2020-04-03T20:58:28+08:00"><meta property="article:modified_time" content="2020-09-24T18:06:32+08:00">
<meta name=twitter:title content="领域逻辑的组织模式">
<meta name=twitter:description content="目前领域逻辑的组织模式分为三种，“事务脚本”，“领域模型” 以及 “表模块”。
事务脚本类似于面向过程编程，事务脚本有以下优点：
 它是一种大多数软件工程师都能理解的简单过程模型。 它能和一个行数据入口或表数据入口简单的数据源很好的协作。 非常容易设定事务的边界。  一个组数据源操作便是一个独立的事务脚本。 当然事务脚本也存在很大的缺陷，当领域逻辑开始变得复杂时，这些缺点就开始暴露出来。 当几个事务要执行类似的逻辑时，通常几个脚本中会含有某些相同的代码。 通过将这些代码提取出来，来形成公共的子例程，来消除这种情况。 但是，很多时候消除副本会变得棘手，而检测副本则更困难，倒是消除副本后的程序反而比以前还要杂乱无章，难以维护。
复杂的领域逻辑，必然要引入对象，解决前面描述问题的面向对象方法就是领域模型。 一个内容管理系统会有用户，文章等类，进行鉴权，以及写入等逻辑均置于领域模型中。 因此，发布对象调用一次写入方法。 可能还会有其他例程来完成一些读取功能，但它其实都是调用领域模型中已有打方法实现的。
 领域模型的控制不再是由一个过程来控制用户某一个动作的逻辑，而是由每个对象都承担一部分相关逻辑。
 领域模型的开销来自于数据源的复杂度和使用上的复杂性，刚刚接触领域模型的人会需要时间来适应这种思维方式。一旦习惯了，你就会很爽！ 另一方面你需要将数据源映射到领域模型上，数据源越是复杂，领域模型的效果就越是显著。
上为事务脚本
上为领域模型
第三中为领域逻辑的组织模式为表模块，它处于事务脚本和领域模型的一个中间地带。 和领域模型最大的区别就是在表模块中一个表只对应一个实例，而领域模型一行数据便能对应一个实例。
表模块的优点在于可以很容易的和软件架构中已经存在的部分衔接，很多GUI应用都是假定将其与SQL查询结果的记录集结果协同工作的。表模块就工作在记录集之上。你可以很容易的使用。
抉择 别问，问就，直接使用领域模型。
接下来我稍微介绍一下目前各个框架/库在领域逻辑的组织模式上的选择（只列出我用）：
  PHP
 PHP 原生 &amp;lt;事务脚本&amp;gt; ThinkPHP &amp;lt;领域模型&amp;gt; Laravel &amp;lt;领域模型&amp;gt; YII &amp;lt;领域模型&amp;gt;    Java
 java.sql.* &amp;lt;事务脚本&amp;gt; MyBatis &amp;lt;表模块&amp;gt; JPA &amp;lt;领域模型&amp;gt;    Go
 gorm &amp;lt;表模块 | 领域模型&amp;gt; （这个比较神奇）    现在用表模块的人普遍比较多，我曾遇到好几个J2EE工程师都并不喜欢JPA的思维模式。">
</head><body class=article-page>
<script>(function(){const e="StackColorScheme";localStorage.getItem(e)||localStorage.setItem(e,"auto")})()</script><script>(function(){const t="StackColorScheme",e=localStorage.getItem(t),n=window.matchMedia("(prefers-color-scheme: dark)").matches===!0;e=="dark"||e==="auto"&&n?document.documentElement.dataset.scheme="dark":document.documentElement.dataset.scheme="light"})()</script>
<div class="container main-container flex on-phone--column compact"><aside class="sidebar left-sidebar sticky">
<button class="hamburger hamburger--spin" type=button id=toggle-menu aria-label="Toggle Menu">
<span class=hamburger-box>
<span class=hamburger-inner></span>
</span>
</button>
<header>
<figure class=site-avatar>
<a href=/>
<img src=/img/avatar_hu8e30117ca872857dd9f41f234a693048_441529_300x0_resize_box_3.png width=300 height=300 class=site-logo loading=lazy alt=Avatar>
</a>
</figure><div class=site-meta>
<h1 class=site-name><a href=/>SDTTTTT</a></h1><h2 class=site-description>臭鱼烂虾</h2></div></header><ol class=menu id=main-menu>
<div class=menu-bottom-section>
<li id=dark-mode-toggle><svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-toggle-left" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentcolor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z"/><circle cx="8" cy="12" r="2"/><rect x="2" y="6" width="20" height="12" rx="6"/></svg><svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-toggle-right" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentcolor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z"/><circle cx="16" cy="12" r="2"/><rect x="2" y="6" width="20" height="12" rx="6"/></svg>
<span>Dark Mode</span>
</li></div></ol></aside><main class="main full-width">
<article class=main-article>
<header class=article-header>
<div class=article-details>
<div class=article-title-wrapper>
<h2 class=article-title>
<a href=/blog/domain-logic-org-mode/>领域逻辑的组织模式</a>
</h2></div><footer class=article-time>
<div><svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-calendar-time" width="56" height="56" viewBox="0 0 24 24" stroke-width="2" stroke="currentcolor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z"/><path d="M11.795 21H5a2 2 0 01-2-2V7a2 2 0 012-2h12a2 2 0 012 2v4"/><circle cx="18" cy="18" r="4"/><path d="M15 3v4"/><path d="M7 3v4"/><path d="M3 11h16"/><path d="M18 16.496V18l1 1"/></svg>
<time class=article-time--published>Apr 03, 2020</time>
</div><div><svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-clock" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentcolor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z"/><circle cx="12" cy="12" r="9"/><polyline points="12 7 12 12 15 15"/></svg>
<time class=article-time--reading>
1 minute read
</time>
</div></footer></div></header><section class=article-content>
<p>目前领域逻辑的组织模式分为三种，“事务脚本”，“领域模型” 以及 “表模块”。</p><p><code>事务脚本</code>类似于<code>面向过程</code>编程，事务脚本有以下优点：</p><ul>
<li>它是一种大多数软件工程师都能理解的简单过程模型。</li><li>它能和一个行数据入口或表数据入口简单的数据源很好的协作。</li><li>非常容易设定事务的边界。</li></ul><p>一个组数据源操作便是一个独立的事务脚本。
当然事务脚本也存在很大的缺陷，当领域逻辑开始变得复杂时，这些缺点就开始暴露出来。
当几个事务要执行类似的逻辑时，通常几个脚本中会含有某些相同的代码。
通过将这些代码提取出来，来形成公共的子例程，来消除这种情况。
但是，很多时候消除副本会变得棘手，而检测副本则更困难，倒是消除副本后的程序反而比以前还要杂乱无章，难以维护。</p><p>复杂的领域逻辑，必然要引入对象，解决前面描述问题的面向对象方法就是<code>领域模型</code>。
一个内容管理系统会有用户，文章等类，进行鉴权，以及写入等逻辑均置于领域模型中。
因此，发布对象调用一次写入方法。
可能还会有其他例程来完成一些读取功能，但它其实都是调用领域模型中已有打方法实现的。</p><blockquote>
<p>领域模型的控制不再是由一个过程来控制用户某一个动作的逻辑，而是由每个对象都承担一部分相关逻辑。</p></blockquote><p>领域模型的开销来自于数据源的复杂度和使用上的复杂性，刚刚接触领域模型的人会需要时间来适应这种思维方式。一旦习惯了，你就会很爽！
另一方面你需要将数据源映射到领域模型上，数据源越是复杂，领域模型的效果就越是显著。</p><p><img src=/shiwujiaoben.png loading=lazy></p><p>上为事务脚本</p><p><img src=/lingyumoxing.png loading=lazy></p><p>上为领域模型</p><p>第三中为领域逻辑的组织模式为<code>表模块</code>，它处于<code>事务脚本</code>和<code>领域模型</code>的一个中间地带。
和领域模型最大的区别就是在表模块中一个表只对应一个实例，而领域模型一行数据便能对应一个实例。</p><p>表模块的优点在于可以很容易的和软件架构中已经存在的部分衔接，很多GUI应用都是假定将其与SQL查询结果的记录集结果协同工作的。表模块就工作在记录集之上。你可以很容易的使用。</p><p><img src=/tablemodule.png loading=lazy></p><h2 id=抉择>抉择</h2><p><img src=/modelfuzadu.png loading=lazy></p><p>别问，问就，直接使用领域模型。</p><p>接下来我稍微介绍一下目前各个框架/库在领域逻辑的组织模式上的选择（只列出我用）：</p><ul>
<li>
<p>PHP</p><ul>
<li>PHP 原生 &lt;事务脚本></li><li>ThinkPHP &lt;领域模型></li><li>Laravel &lt;领域模型></li><li>YII &lt;领域模型></li></ul></li><li>
<p>Java</p><ul>
<li>java.sql.* &lt;事务脚本></li><li>MyBatis &lt;表模块></li><li>JPA &lt;领域模型></li></ul></li><li>
<p>Go</p><ul>
<li>gorm &lt;表模块 | 领域模型> （这个比较神奇）</li></ul></li></ul><p>现在用表模块的人普遍比较多，我曾遇到好几个J2EE工程师都并不喜欢<code>JPA</code>的思维模式。</p></section><footer class=article-footer>
<section class=article-tags>
<a href=/tags/design/>Design</a>
</section><section class=article-lastmod><svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-clock" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentcolor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z"/><circle cx="12" cy="12" r="9"/><polyline points="12 7 12 12 15 15"/></svg>
<span>
Last updated on Sep 24, 2020 18:06 CST
</span>
</section></footer></article><div id=gitalk-container></div><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/gitalk@1.7.2/dist/gitalk.css>
<script src=https://cdn.jsdelivr.net/npm/gitalk@1.7.2/dist/gitalk.min.js></script>
<script src=https://cdn.jsdelivr.net/npm/blueimp-md5@2.18.0/js/md5.min.js></script>
<script>const gitalk=new Gitalk({clientID:"97eb9ce8ac126f0c7833",clientSecret:"5da440441b500b0b016928640712a1b1a03a5f8f",repo:"sdttttt/sdttttt.github.io",owner:"sdttttt",admin:["sdttttt"],distractionFreeMode:!1,id:md5(location.pathname)});(function(){if(["localhost","127.0.0.1"].indexOf(window.location.hostname)!=-1){document.getElementById("gitalk-container").innerHTML="Gitalk comments not available by default when the website is previewed locally.";return}gitalk.render("gitalk-container")})()</script>
<footer class=site-footer>
<section class=copyright>
&copy;
2022 SDTTTTT
</section><section class=powerby>
Built with <a href=https://gohugo.io/ target=_blank rel=noopener>Hugo</a> <br>
Theme <b><a href=https://github.com/CaiJimmy/hugo-theme-stack target=_blank rel=noopener data-version=3.10.0>Stack</a></b> designed by <a href=https://jimmycai.com target=_blank rel=noopener>Jimmy</a>
</section></footer><div class=pswp tabindex=-1 role=dialog aria-hidden=true>
<div class=pswp__bg></div><div class=pswp__scroll-wrap>
<div class=pswp__container>
<div class=pswp__item></div><div class=pswp__item></div><div class=pswp__item></div></div><div class="pswp__ui pswp__ui--hidden">
<div class=pswp__top-bar>
<div class=pswp__counter></div><button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
<button class="pswp__button pswp__button--share" title=Share></button>
<button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
<button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
<div class=pswp__preloader>
<div class=pswp__preloader__icn>
<div class=pswp__preloader__cut>
<div class=pswp__preloader__donut></div></div></div></div></div><div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
<div class=pswp__share-tooltip></div></div><button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
</button>
<button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
</button>
<div class=pswp__caption>
<div class=pswp__caption__center></div></div></div></div></div><script src=https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js integrity="sha256-ePwmChbbvXbsO02lbM3HoHbSHTHFAeChekF1xKJdleo=" crossorigin=anonymous defer></script><script src=https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js integrity="sha256-UKkzOn/w1mBxRmLLGrSeyB4e1xbrp4xylgAWb3M42pU=" crossorigin=anonymous defer></script><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.css integrity="sha256-c0uckgykQ9v5k+IqViZOZKc47Jn7KQil4/MP3ySA3F8=" crossorigin=anonymous><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.css integrity="sha256-SBLU4vv6CA6lHsZ1XyTdhyjJxCjPif/TRkjnsyGAGnE=" crossorigin=anonymous>
</main></div><script src=https://cdn.jsdelivr.net/npm/node-vibrant@3.1.5/dist/vibrant.min.js integrity="sha256-5NovOZc4iwiAWTYIFiIM7DxKUXKWvpVEuMEPLzcm5/g=" crossorigin=anonymous></script><script type=text/javascript src=/ts/main.js defer></script>
<script>(function(){const e=document.createElement("link");e.href="https://fonts.googleapis.com/css2?family=Lato:wght@300;400;700&display=swap",e.type="text/css",e.rel="stylesheet",document.head.appendChild(e)})()</script>
</body></html>